package top.lyjwn.todo.aop.permission;

import cn.hutool.core.util.ClassUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.web.bind.annotation.RequestMapping;
import top.lyjwn.todo.aop.log.controller.ControllerLog;
import top.lyjwn.todo.aop.permission.model.PermissionInfo;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class PermissionScanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {


    private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
        return importingClassMetadata.getClassName() + "#" + PermissionScanDefinitionRegistrar.class.getSimpleName() + "#" + index;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
//        scanner.getBeanDefinitionDefaults()
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(PermissionScan.class.getName()));
        this.registerBeanDefinitions(importingClassMetadata, registry, annoAttrs);

    }


    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, AnnotationAttributes annoAttrs) {
//        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
//        scanner.getBeanDefinitionDefaults()
//        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(PermissionScan.class.getName()));

        String[] values = annoAttrs.getStringArray("value");
        String[] basePackages = annoAttrs.getStringArray("basePackages");
        Class<?>[] basePackageClasses = annoAttrs.getClassArray("basePackageClasses");
        Set<Class<?>> classes = new HashSet<>();
        for (String value : values) {
            Set<Class<?>> s = ClassUtil.scanPackage(value);
            classes.addAll(s);
        }
        for (String value : basePackages) {
            Set<Class<?>> s = ClassUtil.scanPackage(value);
            classes.addAll(s);
        }
        for (Class<?> s : basePackageClasses) {
            classes.add(s);
        }
        PermissionPool permissionPool = new PermissionPool();
        classes.forEach(item -> {

            Class aClass = null;
            try {
                aClass = Class.forName(item.getName());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            String[] classUrls = new String[]{""};
            RequestMapping ClassRequestMapping = (RequestMapping) aClass.getAnnotation(RequestMapping.class);
            if (ClassRequestMapping != null) {
                classUrls = ClassRequestMapping.value();
            }
            Method[] methods = aClass.getMethods();
            for (String classUrl : classUrls) {
                for (Method method : methods) {
                    //ExemptPermissionVerify exemptPermissionVerify = method.getAnnotation(ExemptPermissionVerify.class);
                    if (!method.isAnnotationPresent(ExemptPermissionVerify.class) || method.getAnnotation(ExemptPermissionVerify.class).required() == false) {
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        if (requestMapping != null) {
                            String[] urls = requestMapping.path() == null || requestMapping.path().length == 0 ? requestMapping.value() : requestMapping.path();
                            for (String url_it : urls) {
                                String url = classUrl + url_it;
                                String url_replace = url.replace("\\", "/");
                                String[] url_replace_split = url_replace.split("/");
                                List<String> urlPaths = new ArrayList<>();
                                for (String s : url_replace_split) {
                                    if (s != null && !"".equals(s)) {
                                        urlPaths.add(s);
                                    }
                                }
                                PermissionInfo permissionInfo = new PermissionInfo();
                                permissionInfo.setUrl(url);
                                permissionInfo.setMethodName(method.getName());
                                ControllerLog controllerLog = method.getAnnotation(ControllerLog.class);
                                if (controllerLog != null) {
                                    permissionInfo.setMark(controllerLog.description() == null || "".equals(controllerLog.description()) ? controllerLog.value() : controllerLog.description());
                                }
                                permissionInfo.setCode(StringUtils.join(urlPaths.toArray(), ':'));
                                permissionPool.getCodePool().put(permissionInfo.getCode(), permissionInfo);
                                permissionPool.getUrlPool().put(permissionInfo.getUrl(), permissionInfo);
                            }

                        }
                    }
                }
            }


        });
//            DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        BeanDefinitionBuilder b = BeanDefinitionBuilder.genericBeanDefinition(PermissionPool.class)
                .addPropertyValue("codePool", permissionPool.getCodePool())
                .addPropertyValue("urlPool", permissionPool.getUrlPool());
        registry.registerBeanDefinition(generateBaseBeanName(importingClassMetadata, 0), b.getBeanDefinition());
    }

    static class PermissionScanRepeatingRegistrar extends PermissionScanDefinitionRegistrar {
        PermissionScanRepeatingRegistrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            AnnotationAttributes mapperScansAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(PermissionScans.class.getName()));
            if (mapperScansAttrs != null) {
                AnnotationAttributes[] annotations = mapperScansAttrs.getAnnotationArray("value");

                for (int i = 0; i < annotations.length; ++i) {
                    this.registerBeanDefinitions(importingClassMetadata, registry, annotations[i]);
                }
            }

        }
    }

}
